package be.nepherte.movmanager.client.ui.components;

import java.util.List;

import be.nepherte.movmanager.client.MovManager;
import be.nepherte.movmanager.client.exceptions.InvalidMovieDurationException;
import be.nepherte.movmanager.client.exceptions.InvalidMovieException;
import be.nepherte.movmanager.client.exceptions.InvalidMovieRatingException;
import be.nepherte.movmanager.client.exceptions.InvalidMovieReleaseDateException;
import be.nepherte.movmanager.client.exceptions.InvalidMovieTitleException;
import be.nepherte.movmanager.client.util.StringUtil;
import be.nepherte.movmanager.domain.Movie;

import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.TextBoxBase;
import com.google.gwt.user.client.ui.Widget;

/**
 * This class represents a grid that contains a {@link Movie}.
 * 
 * <p>
 * This file is part of MovManager.
 * </p>
 * 
 * <p>
 * MovManager is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * </p>
 * 
 * <p>
 * MovManager is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * </p>
 * 
 * <p>
 * You should have received a copy of the GNU General Public License along with
 * MovManager. If not, see <a
 * href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses</a>.
 * </p>
 * 
 * @author	Bart Verhoeven
 * @version	0.1
 */
public class MovieGrid extends Grid implements HasMovie, KeyPressHandler {
	/**the movie this movie grid currently contains.*/
	private Movie movie;
	/**Text box for movie title.*/
	private final TextBox txtTitle = new TextBox();
	/**Text box for movie description.*/
	private final TextArea txtDescription = new TextArea();
	/**Integer box for movie duration.*/
	private final IntegerBox txtDuration = new IntegerBox();
	/**Date box for movie release date.*/
	private final DateBox txtDate = new DateBox();
	/**Double box for movie rating.*/
	private final DoubleBox txtRating = new DoubleBox();
	/**Text box for movie directors.*/
	private final TextBox txtDirectors = new TextBox();
	/**Text box for movie writers.*/
	private final TextBox txtWriters = new TextBox();
	/**Text box for movie studio.*/
	private final TextBox txtStudio = new TextBox();
	/**Text box for movie genres.*/
	private final GenreBox txtGenres = new GenreBox();
	/**Text box for movie poster.*/
	private final TextBox txtPoster = new TextBox();
	/**Whether the values of this movie grid can be edited.*/
	private boolean editable = true;
	
	/**
	 * Creates a new empty movie grid.
	 */
	public MovieGrid() {
		this(null, true);
	}

	/**
	 * Creates a new editable movie grid that displays the given movie. When a
	 * null movie is supplied, the grid will be empty.
	 * 
	 * @param	movie
	 *          the movie that will be filled in this movie grid
	 */
	public MovieGrid(Movie movie) {
		this(movie, true);
	}
	
	/**
	 * Creates a new movie grid that displays the given movie. The grid can be
	 * editable or not.
	 * 
	 * @param	movie
	 *          the movie to be displayed
	 * @param	editable
	 *          whether this movie grid is editable
	 */
	public MovieGrid(Movie movie, boolean editable) {
		super(10,2);
		this.editable = editable;
		setGridHeader();
		setGridWidgets();
		formatGrid();
		setMovie(movie);
	}
	
	/**
	 * Sets the headers for the different properties of a movie. The header is
	 * placed in the first column.
	 */
	private void setGridHeader() {
		super.setText(0, 0, MovManager.constants.movieTitle());
		super.setText(1, 0, MovManager.constants.movieRating());
		super.setText(2, 0, MovManager.constants.movieGenres());
		super.setText(3, 0, MovManager.constants.movieDuration());
		super.setText(4, 0, MovManager.constants.movieDirectors());
		super.setText(5, 0, MovManager.constants.movieWriters());
		super.setText(6, 0, MovManager.constants.movieReleaseDate());
		super.setText(7, 0, MovManager.constants.movieStudio());
		super.setText(8, 0, MovManager.constants.moviePoster());
		super.setText(9, 0, MovManager.constants.movieDescription());
	}
	
	/**
	 * Sets the different text boxes next to their corresponding headers.
	 * Text boxes are placed in the second column.
	 */
	private void setGridWidgets() {
		super.setWidget(0, 1, txtTitle);
		super.setWidget(1, 1, txtRating);
		super.setWidget(2, 1, txtGenres);
		super.setWidget(3, 1, txtDuration);
		super.setWidget(4, 1, txtDirectors);
		super.setWidget(5, 1, txtWriters);
		super.setWidget(6, 1, txtDate);
		super.setWidget(7, 1, txtStudio);
		super.setWidget(8, 1, txtPoster);
		super.setWidget(9, 1, txtDescription);
	}
	
	/**
	 * Formats this movie grid.
	 */
	private void formatGrid() {
		super.setCellSpacing(10);
		txtDescription.setHeight("90px");
		setToolTips();
		
		// loops over the rows of this movie grid
		CellFormatter cf = super.getCellFormatter();
		RowFormatter rf = super.getRowFormatter();
		for (int i = 0; i < super.getRowCount(); i++) {
			// column independent
			if (!editable)
				rf.setVisible(8, false);
			// column 0
			cf.addStyleName(i, 0, "boldStyle");
			cf.setHorizontalAlignment(i, 0, HasHorizontalAlignment.ALIGN_RIGHT);
			cf.setVerticalAlignment(i, 0, HasVerticalAlignment.ALIGN_TOP);
			// column 1
			Widget w = super.getWidget(i, 1);
			w.setWidth("300px");
			if (w instanceof TextBoxBase)
				((TextBoxBase) super.getWidget(i, 1)).addKeyPressHandler(this);
		}
	}

	/**
	 * Sets the tool tips to text box widgets.
	 */
	private void setToolTips() {
		txtRating.setTitle(MovManager.constants.movieGridRatingTooltip());
		txtDate.setTitle(MovManager.constants.movieGridDateTooltip());
		txtDirectors.setTitle(MovManager.constants.movieGridDirectorsTooltip());
		txtWriters.setTitle(MovManager.constants.movieGridWritersTooltip());
		txtDuration.setTitle(MovManager.constants.movieGridDurationTooltip());
		txtGenres.setTitle(MovManager.constants.movieGridGenresTooltip());
		txtPoster.setTitle(MovManager.constants.movieGridPosterTooltip());
	}


	/**
	 * Sets the given movie in the movie grid. When movie is null, no movie will be displayed.
	 */
	@Override
	public void setMovie(Movie movie) {
		this.movie = movie;
		
		// clean this movie grid if movie is null
		if (movie == null) {
			reset();
			return;
		}
		
		txtTitle.setText(movie.getTitle());
		txtRating.setValue(movie.getRating());
		txtGenres.setValue(movie.getGenres());
		txtDuration.setValue(movie.getDuration());
		txtDirectors.setText(StringUtil.toString(movie.getDirectors(), ", "));
		txtWriters.setText(StringUtil.toString(movie.getWriters(), ", "));
		txtDate.setValue(movie.getReleaseDate());
		txtStudio.setText(movie.getStudio());
		txtDescription.setText(movie.getDescription());
		txtPoster.setText(movie.getMoviePoster());
	}

	@Override
	public Movie getMovie() throws InvalidMovieException {
		List<String> directors = StringUtil.split(txtDirectors.getText(), ",");
		List<String> writers = StringUtil.split(txtWriters.getText(), ",");
		
		// if there currently is a movie, edit that one instead
		if (movie == null)
			movie = new Movie();
			
		try {
			movie.setTitle(txtTitle.getText());
			movie.setRating(txtRating.getValue());
			movie.setGenres(txtGenres.getValue());
			movie.setDuration(txtDuration.getValue());
			movie.setDirectors(directors);
			movie.setWriters(writers);
			movie.setReleaseDate(txtDate.getValue());
			movie.setStudio(txtStudio.getText());
			movie.setDescription(txtDescription.getText());
			movie.setMoviePoster(txtPoster.getText());
		} catch (InvalidMovieTitleException e) {
			MessagePopupPanel pp = new MessagePopupPanel(e.getMessage(), "ppError");
			pp.setPopupPosition(txtTitle.getAbsoluteLeft()+310, txtTitle.getAbsoluteTop());
			pp.show();
			throw e;
		} catch (InvalidMovieRatingException e) {
			MessagePopupPanel pp = new MessagePopupPanel(e.getMessage(), "ppError");
			pp.setPopupPosition(txtRating.getAbsoluteLeft()+310, txtRating.getAbsoluteTop());
			pp.show();
			throw e;
		} catch (InvalidMovieDurationException e) {
			MessagePopupPanel pp = new MessagePopupPanel(e.getMessage(), "ppError");
			pp.setPopupPosition(txtDuration.getAbsoluteLeft()+310, txtDuration.getAbsoluteTop());
			pp.show();
			throw e;
		} catch (InvalidMovieReleaseDateException e) {
			MessagePopupPanel pp = new MessagePopupPanel(e.getMessage(), "ppError");
			pp.setPopupPosition(txtDate.getAbsoluteLeft()+310, txtDate.getAbsoluteTop());
			pp.show();
			throw e;
		}
		return movie;
	}
	
	/**
	 * Resets this movie grid: the current movie will be cleared.
	 */
	public void reset() {
		movie = null;
		
		for (int i = 0; i < super.getRowCount(); i++) {
			HasValue<?> v = (HasValue<?>) super.getWidget(i, 1);
			v.setValue(null);
		}
	}

	/**
	 * Cancels all key presses in the text boxes of this movie grid when it's
	 * not editable.
	 */
	@Override
	public void onKeyPress(KeyPressEvent event) {
		Object o =event.getSource();
		if (!editable && o instanceof TextBoxBase)
			((TextBoxBase) o).cancelKey();
	}
}